NewBeginnings:DataModel
Now that we know how to make a client application and the server talk together, we can now explore further. I'll take for granted (you might have noticed that I take a lot of things for granted when it comes the my public!) that you all have some experience with databases. If you're like me, you had experience with traditionnal relational databases, such as MySQL, SQLite, Access or others. If you come from the relational world, you are used to SQL queries and all the problems you usually encounter with them. When programming in any language before, you had to use some programming magic to build dynamic queries that would adapt to the active content, etc. I'm far from being a database expert and I won't pretend having any absolute truth about the subject. All I have is the sum of my past experiences. And to my knowledge, databases are a blessing and a curse all at once. In a relational model, you have to think of everything all at once. You have to make a plan for the lopng term and follow it tightly because any mistake or anything left behind will become an obstacle at some point because restructuring the model is complex, even with tools such as Rails, etc. Also, when it comes to big data structures with tables containing thousands of entities, you have to think about everydetail to make the query process faster and less ressource-hungry on the server. No more SQL! The good news with Wakanda is that you can all forget about it right away. This is no relational database, even if it may look like it because of the automated process behind the Model Editor. Welcome to the world of the NOSQL (Not Only SQL). A few years ago, when the data needed by some internet giants, such as Google, became too big and too changing to suit the possibilities of a relational database they started thinking out of the box and came up with something that was based on the entities and not on the model. I won't give a class about the whole concept, but there are some really interesting articles about it on Wikipedia and other websites. The main thing to remember from this is that you won't need to do any magic tricks to build a dynamic query anymore. Everything can now be defined visually in the Studio and all you will ever need is javascript. Exploring the Wakanda model Lets start by adding a new page to our project and call it "DataModel". Once it's done, open the "Model" item in the project tree-view to load the model editor. As we already seen in the second chapter, there is a data class already made for us and named "DataClass1". Rename the class to "Contact" and add fields in it by pressing the "+" icon beside the "Attributes" header. It will add an element to the class model and let you enter the name and datatype of the new attribute. For all of you coming from a strong OOP background, you can se that part as building your object defintion. You will probably notice the "Methods" header. I'll come back on this later, but in short, you can define methods that will let you manipulate the data in the entity or return a value for what we call in Wakanda, a "calculated attribute". A good example of that will soon follow! In the class properties panel, you may also want to rename the collection name to something easier to read. I like my class names to be as short as possible (in that case "Contact", a good name representing what a single entity is) and the collection name as easy to read as possible (in this case I named it "Contacts", a good name that represents multiple entities of the same type). Once you are done with the model, save it and don't forget to hit the "Reload Models" button in the main toolbar. If you don't reload the models to the server, you won't be able to work with any change you've done. When the model is reloaded, go back to the GUI Designer for the "DataModel" page and take another look to the left panel. Linking the model and the GUI The nicest thing about Wakanda is how much they automated everything when it comes to the database. In the left panel of the GUI Designer, below the widget toolbox, you should now see, under the "Model" section, the class you created in the model editor. If it's not there, make sure that you reloaded the models to the server. If you go under the "Datasources" tab, there should be nothing there yet. Let me explain the difference between the datastore classes and the datasources themselves. The Datastore Classes are definition of your data model on the server. A datasource, on the other hand, is an object that will acces a datastore for you. While the datastore classes are unique items available through your whole project (you cannot have multiple definitions of a single object), you can have as many datasources you want linked to a single class. This is where it may get confusing, but I will come back on this later. So back to our page! There is two ways you can create a datasource in Wakanda. You can do it manually or let Wakanda do it for you. We'll let the software help you and you'll now witness the magic happening. Simply drag the class model to your page and drop it there. Yes, it's all you need to do! A dialog will pop to let you decide how you want . Just make it look like this and hit the "Create" button. A grid-view will be created for you and you can now resize it and move it around so you can see all the columns. Once it's done, you can save your page and run it in the browser. This is all you have to do to have a basic database application running. Play with it and add some data to the grid with the buttons already provided in the widget. The datasource that will provide the data for the grid was created automatically (you can look in the Datasources tab if you don't believe me) and the database link is working by itself. Filtering the data and fine tuning the model If you tried to add an empty element, you'll notice that nothing stopped you from doing so. This is not good because everyone should have a name, right? So go back to the model editor, select the "firstname" and the "lastname" attributes of your class and make sure that the "Mandatory" property is checked for both of them, in the properties panel. You'll see that you have a different property panel when an attribute is selected, when the whole class is selected or when nothing (or if you prefer, the whole model) is selected. Once it's done, you'll have to save and reload the model to the server before you try to input some empty entities. Any change you make to the model requires you to reload it. If you go back to your page and try to save an empty entity in the grid, you should see something like this happen. This is how Wakanda handles the database errors by default. You can also build your own error handling process and decide how the user will be notified that he did something wrong, but more on this in another chapter. So, we now have a functionnal but very basic contact book. Let's now add some functionnalities to that grid so we can filter what data we want to be displayed. First of all, lets give that grid a respectful ID, something like "grdContacts". Then, under the grid, add a checkbox and name it "chkFriendsOnly". It will be used to decide if we show only the people we marked as our friends or everybody. Create the OnChange event for the checkbox and add the following code to it: chkFriendsOnly.change = function chkFriendsOnly_change (event) { if ( this.getValue() ) //TRUE if checked { sources.contact.query('friend true'); } else { sources.contact.query(); } }; The "sources" object is a namespace for all the datasources on the current page. The rest is pretty much straight forward. The query() function is self-explanatory and the query language used, as you can see, is a simple boolean operation. Also notice that we didn't access the checkbox with the $$() function as we did before. This is because we are in the context of the widget and the "this" object can be used to access it directly. A little more on the datasource mechanics If you run the page in the browser, you will see that when we toggle the checkbox, the population of the grid changes to reflect our query. The thing is that we never said anything to that grid. This is the power of the datasources. The grid, if you look in the properties panel, is linked to your datasource with the "Source" property. As the grid is a complex widget, it's all it needs to populate itself. The columns definition at the botom of the panel let you decide what data you will display and how. Because the grid is linked to the datasource, it will update itself following the result that datasource will give it. Everything is automated in the background so you don't have to worry about anything more than asking for the right thing. This is useful because you can add more widgets later on that will be linked to the same datasource and they will all get updated at the same time. This is also why you may want multiple datasources associated to the same datastore. For example, if you want to have a grid with all your friends on one side and another grid with every "John" in your contact list on the other side, you will need two different queries. As a datasource will update all the linked widgets when you query it, you can't use a single source to give two different result sets to populate different grids. You will need two datasources with their very own queries and linked to their respective grids. If the picture isn't clear yet, you will get there soon enough. Restricting queries and inheritance There is some really powerful tools in Wakanda and Restricting Queries are one of them. Lets find out how it works and discover some other nice features of Wakanda. Go back to the model editor and right click yout "Contact" class. The drop down menu will offer you two possibilities: Extend or Hide. Hit Extend and see what happens. It creates a new data class that you will name "Friend". If like me you tend to approach data structures in a OOP class/object state of mind, you will really like this and you probably noticed the small header that says "Inherited from Contact". Yes it's true, Wakanda supports inheritance up to some extent. At the moment there is no "true parenting" because all you can add to a descending class are methods, calculated attributes and relationships. But real parenting is on the way and when it happens, it will change our lives. So, back to the restricting query. Select the "Friend" class and go to the properties panel. In the "Restricting Query" field enter the following code: friend true Because the "Friend" class is extended from the "Contact" class, they will refer to the same entities collection. This is where it becomes interesting because without nothing more than what you did, you now have created a kind of shortcut to filter your data. Any datasource linked to this class will return contact entities, but only those with their "friend" attribute set to TRUE. You can then use filter queries on that datasource to refine the results but only on the Friend subset of data. Now, save and reload your model and go back to the GUI designer. You will see the new class appear in the Model tab. Drag it on the page to add a new grid called "grdFriends" and leave only the first and last name columns. Move and resize the grid at will and then run the page in the browser. You will see it in action right away: your new grid showing only the people you marked as friends. But what happens if you check someone as friend in the first grid? It will save the data to the datastore, but the new grid will not display the new friend. This is because that grid is linked to a different datasource, and even though they both interact with the same set of entities, the "friend" datasource was never ask to update its results. Let's fix this quickly by selecting the "contact" datasource in the left panel (not the model class) and by creating a new attribute event. The datasource events let you automatize your process easily. So for our needs, we need to update the "friend" datasource when the "friend" attribute changes in the "contact" datasource. So create a new OnFriendAttributeChange event and add the following code to it: contactEvent.onfriendAttributeChange = function contactEvent_onfriendAttributeChange (event) { sources.friend.query(); // Force the datasource to requery itself }; As simple as that!